package com.durian.base.rxhttp.param

import com.durian.base.rxhttp.RxHttp
import com.durian.base.rxhttp.await.AwaitImpl
import com.durian.base.rxhttp.await.IAwait
import com.durian.base.rxhttp.download.*
import com.durian.base.rxhttp.download.setRangeHeader
import com.durian.base.rxhttp.parse.DefaultListParser
import com.durian.base.rxhttp.parse.DefaultParser
import com.durian.base.rxhttp.parse.Parser
import com.durian.base.rxhttp.parse.SuspendStreamParser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.*
import okhttp3.OkHttpClient
import okhttp3.Response
import java.io.File
import java.io.IOException

/**
 * 说明：IRxHttp
 * <p/>
 * 作者：fanly
 * <p/>
 * 类型：Class
 * <p/>
 * 时间：2021/10/20 9:01 下午
 * <p/>
 * 版本：version 1.0
 */
interface IRxHttp

/**
 * toParser
 */
inline fun <T> IRxHttp.toParser(
    parser: Parser<T>,
    client: OkHttpClient = RxHttp.getOkHttpClient()
): IAwait<T> = AwaitImpl(this as IParam<T>, parser, client)

/**
 * asList
 */
inline fun <reified T : Any> IRxHttp.asList() = toParser(object : DefaultListParser<T>() {})

/**
 * asClass
 */
inline fun <reified T : Any> IRxHttp.asClass() = toParser(object : DefaultParser<T>() {})

/**
 * asString
 */
inline fun IRxHttp.asString() = asClass<String>()

/**
 * asResponse
 */
inline fun IRxHttp.asResponse(checkResponse: Boolean = true) =
    toParser(object : DefaultParser<Response>() {
        @Throws(IOException::class)
        override fun onParse(response: Response): Response {
            if (checkResponse) {
                checkResponse(response)
            }
            return response
        }
    })

/**
 * 下载文件
 */
fun IRxHttp.toDownload(
    destFile: File,
    append: Boolean = false,
    capacity: Int = 1,
    progress: suspend (Progress) -> Unit
): Flow<String> = toDownload(destFile.absolutePath,append,capacity,progress)

/**
 * 下载文件
 */
fun IRxHttp.toDownload(
    destPath: String,
    append: Boolean = false,
    capacity: Int = 1,
    progress: suspend (Progress) -> Unit
): Flow<String> = toDownload(FileOutputStreamFactory(destPath),append,capacity,progress)


/**
 * 下载文件
 */
fun <T> IRxHttp.toDownload(
    osFactory: OutputStreamFactory<T>,
    append: Boolean = false,
    capacity: Int = 1,
    progress: suspend (Progress) -> Unit
): Flow<T> = kotlin.run {
    if (progress == null) {
        flow {
            setRangeHeader(osFactory, append)
            emit(toSyncDownload(osFactory).await())
        }.flowOn(Dispatchers.IO)
    } else {
        toFlowProgress(osFactory, append, capacity).onEachProgress(progress)
    }
}

fun <T> IRxHttp.toSyncDownload(
    osFactory: OutputStreamFactory<T>,
    progressCallback: (suspend (ProgressT<T>) -> Unit)? = null
): IAwait<T> {
    val parser = SuspendStreamParser(osFactory)
    if (progressCallback != null) {
        parser.progress = { progress, currentSize, totalSize ->
            //LogUtil.logDownProgress(progress, currentSize, totalSize)
            val p = ProgressT<T>(progress, currentSize, totalSize)
            progressCallback(p)
        }
    }
    return toParser(parser)
}

fun <T> IRxHttp.toFlowProgress(
    osFactory: OutputStreamFactory<T>,
    append: Boolean = false,
    capacity: Int = 1
): Flow<ProgressT<T>> = kotlin.run {
    flow {
        setRangeHeader(osFactory, append)
        toSyncDownload(osFactory) { emit(it) }
            .await().let { emit(ProgressT(it)) }
    }.buffer(capacity, BufferOverflow.DROP_OLDEST).flowOn(Dispatchers.IO)
}

fun <T> Flow<ProgressT<T>>.onEachProgress(progress: suspend (Progress) -> Unit): Flow<T> =
    onEach { if (it.getResult() == null) progress(it) }.mapNotNull { it.getResult() }